package com.hontech.pastacooking.conn

import com.hontech.pastacooking.app.ConnTask
import com.hontech.pastacooking.app.PromiseTask
import com.hontech.pastacooking.app.Task
import com.hontech.pastacooking.app.log
import com.hontech.pastacooking.except.AckException
import com.hontech.pastacooking.serial.SerialType
import com.hontech.pastacooking.utils.Sync
import java.io.IOException

object Device {

    private const val BaudRate = 38400
    //  const val BaudRate = 115200

    private var mPort: SerialPort? = null
    private var mReader: ReaderTask? = null
    private val syncAck = Sync()

    fun open(name: String) {
        close()
        val port = SerialPort.open2(name, BaudRate)
        val reader = ReaderTask(port, syncAck)
        reader.start()
        mPort = port
        mReader = reader

        log("串口打开:$name")
    }

    fun isOpen(): Boolean {
        return mPort?.isOpen ?: false
    }

    fun close() {
        if (isOpen()) {
            mPort?.close()
            mReader?.join()
            log("串口关闭")
        }
        mReader = null
        mPort = null
    }

    private fun write(buf: ByteArray) {
        if (!isOpen()) {
            throw IOException("串口没有打开")
        }
        syncAck.clear()
        mPort!!.write(buf)
        val ret = syncAck.await(500)
        if (!ret) {
            throw AckException("通信异常没有收到ack")
        }
    }

    private suspend fun write(dest: Int, req: Int, args: Array<SerialType>) {
        PromiseTask {
            val buf = Proto.make(dest, req, args)
            write(buf)
        }.exec(ConnTask).await()
    }

    private fun writeAck(dest: Int) {
        Task {
            val buf = Proto.make(dest, Proto.Ack, arrayOf())
            mPort!!.write(buf)
        }.exec(ConnTask)
    }

    suspend fun send(timeout: Long, dest: Int, req: Int, args: Array<SerialType>): Frame {
        mReader?.begin(dest, req) ?: throw IOException("串口没有工作")
        try {
            write(dest, req, args)
            val frame = mReader?.wait(timeout) ?: throw IOException("串口工作异常")
            if (Proto.isNeedRespAck(dest, req)) {
                writeAck(dest)
            }
            return frame
        } catch (e: Exception) {
            throw e
        } finally {
            mReader?.clear()
        }
    }

    suspend fun request(
        timeout: Long,
        dest: Int,
        req: Int,
        args: Array<SerialType>,
        exceptMsg: String
    ) {
        val frame = send(timeout, dest, req, args)
        frame.checkErrResp(dest, exceptMsg)
    }

    suspend fun requestKeep(exceptMsg: String) {
        mReader?.beginKeep() ?: throw IOException("串口没有工作")
        try {
            write(Addr.Main, MainProto.Keepalive, emptyArray())
            val frame = mReader?.waitKeep() ?: throw IOException("串口工作异常")
            frame.checkErrResp(Addr.Main, exceptMsg)
        } catch (e: Exception) {
            throw e
        } finally {
            mReader?.clearKeep()
        }
    }
}















